﻿/**
 * Copyright (C) 2013 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Spi;
using NGuice.Inject.Internal.Util;
using System.Reflection;

namespace NGuice.Inject.Internal
{

    /// <summary>
    /// 通过类型的构造注入器
    /// </summary>
    internal sealed class ConstructorInjectorStore
    {
        private readonly InjectorImpl injector;

        private readonly FailableCache<InjectionPoint, ConstructorInjector> cache;

        public ConstructorInjectorStore(InjectorImpl injector)
        {
            this.injector = injector;
            cache = new FailableCacheInternal(this);
        }

        /// <summary>
        /// 获取一个新的完成的注入器，此注入器中注册了注入监听器
        /// </summary>
        /// <param name="constructorInjector"></param>
        /// <param name="errors"></param>
        /// <returns></returns>
        /// <exception cref="ErrorsException"></exception>
        public ConstructorInjector Get(InjectionPoint constructorInjector, Errors errors)
        {
            return cache.Get(constructorInjector, errors);
        }


        /// <summary>
        /// 从缓存中清除注入点。仅当缓存不真实有效且需要清除时使用(See issue 319 and
        /// ImplicitBindingTest#testCircularJitBindingsLeaveNoResidue and
        /// #testInstancesRequestingProvidersForThemselvesWithChildInjectors for examples of when this is
        /// necessary.)
        /// </summary>
        /// <param name="ip"></param>
        /// <returns>如果存储在缓存中的注入器点清除则返回true</returns>
        internal bool Remove(InjectionPoint ip)
        {
            return cache.Remove(ip);
        }

        internal ConstructorInjector<T> CreateConstructor<T>(InjectionPoint injectionPoint, Errors errors)
        {
            int numErrorsBefore = errors.Size;

            SingleParameterInjector[] constructorParameterInjectors
                = injector.GetParametersInjectors(injectionPoint.Dependencies, errors);

            // the injector type agrees with the injection point type
            MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>)injector.membersInjectorStore
                .Get<T>(injectionPoint.DeclaringType, errors);

            /*if[AOP]*/
            IList<MethodAspect> injectorAspects = injector.state.MethodAspects;
            IList<MethodAspect> methodAspects = membersInjector.AddedAspects.IsEmpty
                ? injectorAspects
                : ImmutableList<MethodAspect>.CopyOf<MethodAspect>(Lists.Concat<MethodAspect>(injectorAspects, membersInjector.AddedAspects));
            ConstructionProxyFactory<T> factory = new ProxyFactory<T>(injectionPoint, methodAspects);
            /*end[AOP]*/
            /*if[NO_AOP]
            ConstructionProxyFactory<T> factory = new DefaultConstructionProxyFactory<T>(injectionPoint);
            end[NO_AOP]*/

            errors.ThrowIfNewErrors(numErrorsBefore);

            return new ConstructorInjector<T>(membersInjector.GetInjectionPoints(), factory.Create(),
                constructorParameterInjectors, membersInjector);
        }

    }

    internal class FailableCacheInternal : FailableCache<InjectionPoint, ConstructorInjector>
    {
        private ConstructorInjectorStore outer;

        public FailableCacheInternal(ConstructorInjectorStore outer)
        {
            this.outer = outer;
        }

        protected override ConstructorInjector Create(InjectionPoint constructorInjector, Errors errors)
        {
            //TODO:无法指定T泛型参数，只能通过反射调用，泛型类型为injectionPoint.DeclaringType的泛型类型
            MethodInfo method = outer.GetType().GetMethod("CreateConstructor", BindingFlags.Instance | BindingFlags.NonPublic);
            return method.MakeGenericMethod(constructorInjector.DeclaringType)
                         .Invoke(outer, new object[] { constructorInjector, errors }) as ConstructorInjector;
            //return outer.CreateConstructor(constructorInjector, errors);
        }
    }
}
